﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Client.TopLevel
{
    #region Top-level Objects

    public class TLPosition
    {
        public int    Id     { get; set; }
        public string Name   { get; set; }
        public int    Salary { get; set; }

        public TLPosition()
        {
            Id      = Defaults.DEFAULT_INT;
            Name    = Defaults.DEFAULT_STR;
            Salary  = Defaults.DEFAULT_INT;
        }
        
        public TLPosition(int id, string name, int salary)
        {
            Id      = id;
            Name    = name;
            Salary = salary;
        }

        public TLPosition(Position position)
        {
            Id      = position.Id;
            Name    = position.Name;
            Salary  = position.Salary;
        }

        internal Position ToPosition()
        {
            return new Position(Id, Salary, Name);
        }

        public override string ToString()
        {
            return String.Concat(Name, " (", Salary, " р.)");
        }
    }

    public class TLWorkTime
    {
        public DateTime TimeIn  { get; set; }
        public DateTime TimeOut { get; set; }

        public TLWorkTime()
        {
            TimeIn  = DateTime.Now;
            TimeOut = DateTime.Now;
        }

        public TLWorkTime(DateTime timeIn, DateTime timeOut)
        {
            TimeIn  = timeIn;
            TimeOut = timeOut;
        }

        internal TLWorkTime(TimeTable timeTable)
        {
            TimeIn  = Convert.ToDateTime(timeTable.TimeIn);
            TimeOut = Convert.ToDateTime(timeTable.TimeOut);
        }
    }

    public class TLEmployee
    {
        public const byte ROLE_EMPLOYEE = Defaults.ROLE_EMPLOYEE;
        public const byte ROLE_MANAGER  = Defaults.ROLE_MANAGER;

        public int    Id            { get; set; }
        public string Name          { get; set; }
        public string Login         { get; set; }
        public string Password      { get; set; }
        public int    PositionId    { get; set; }
        public int    BankAccountId { get; set; }
        public byte   Role          { get; set; }

        public TLEmployee()
        {
            Id              = Defaults.DEFAULT_INT;
            Name            = Defaults.DEFAULT_STR;
            Login           = Defaults.DEFAULT_STR;
            Password        = Defaults.DEFAULT_STR;
            Role            = Defaults.DEFAULT_ROLE;
            BankAccountId   = Defaults.DEFAULT_INT;
            PositionId      = Defaults.DEFAULT_INT;
        }

        public TLEmployee(TLEmployee employee)
        {
            Id              = employee.Id;
            Name            = employee.Name;
            Login           = employee.Login;
            Password        = employee.Password;
            Role            = employee.Role;
            BankAccountId   = employee.BankAccountId;
            PositionId      = employee.PositionId;
        }

        public TLEmployee(int id, string name, string login, string password, byte role, int bankAccountId, int positionId)
        {
            Id              = id;
            Name            = name;
            Login           = login;
            Password        = password;
            Role            = role;
            BankAccountId   = bankAccountId;
            PositionId      = positionId;
        }

        internal TLEmployee(Employee employee)
        {
            Id              = employee.Id;
            Name            = employee.Name;
            Login           = employee.Login;
            Password        = employee.Password;
            Role            = employee.Role;
            BankAccountId   = employee.BankAccountId;
            PositionId      = employee.PositionId;
        }

        internal Employee ToEmployee()
        {
            return new Employee(Id, Name, Login, Password, Role, BankAccountId, PositionId);
        }
    }

    public class TLReport
    {
        public TLEmployee       Employee { get; set; }
        public List<TLWorkTime> Time     { get; set; }

        public TLReport()
        {
            Employee = new TLEmployee();
            Time     = new List<TLWorkTime>();
        }

        public TLReport(TLEmployee employee, IEnumerable<TLWorkTime> workTime)
        {
            Employee = new TLEmployee(employee);
            Time     = new List<TLWorkTime>(workTime);
        }
    }

    public class TLTask
    {
        public int        Id              { get; set; }
        public string     Title           { get; set; }
        public DateTime   StartTime       { get; set; }
        public DateTime   EndTime         { get; set; }
        public DateTime   EstimationTime  { get; set; }
        public int        Cost            { get; set; }
        public int        Penalty         { get; set; }
        public int        EmployeeId      { get; set; }

        public TLTask()
        {
            Id              = Defaults.DEFAULT_INT;
            Title           = Defaults.DEFAULT_STR;
            StartTime       = DateTime.Now;
            EndTime         = DateTime.Now;
            EstimationTime  = DateTime.Now;
            Cost            = Defaults.DEFAULT_INT;
            Penalty         = Defaults.DEFAULT_INT;
            EmployeeId      = Defaults.DEFAULT_INT;
        }

        public TLTask(int id, string title, DateTime stTime, DateTime endTime, DateTime estTime, int cost, int penalty, int employeeId)
        {
            Id              = id;
            Title           = title;
            StartTime       = stTime;
            EndTime         = endTime;
            EstimationTime  = estTime;
            Cost            = cost;
            Penalty         = penalty;
            EmployeeId      = employeeId;
        }

        internal TLTask(Task task)
        {
            Id              = task.Id;
            Title           = task.Title;
            StartTime       = Utilities.IsValidDateTime(task.StartTime)      ? Convert.ToDateTime(task.StartTime)      : DateTime.MinValue;
            EndTime         = Utilities.IsValidDateTime(task.EndTime)        ? Convert.ToDateTime(task.EndTime)        : DateTime.MinValue;
            EstimationTime  = Utilities.IsValidDateTime(task.EstimationTime) ? Convert.ToDateTime(task.EstimationTime) : DateTime.MinValue;
            Cost            = task.Cost;
            Penalty         = task.Penalty;
            EmployeeId      = task.EmployeeId;
        }

        internal Task ToTask()
        {
            return new Task(Id, Title, StartTime.ToString(Defaults.DATETIME_FORMAT), EndTime.ToString(Defaults.DATETIME_FORMAT), EstimationTime.ToString(Defaults.DATETIME_FORMAT), Cost, Penalty, EmployeeId);
        }
    }

    public class TLPayment
    {
        public int          Id            { get; set; }
        public int          BankAccountId { get; set; }
        public int          Sum           { get; set; }
        public DateTime     PayTime       { get; set; }
        public int          EmployeeId    { get; set; }

        public TLPayment()
        {
            Id              = Defaults.DEFAULT_INT;
            BankAccountId   = Defaults.DEFAULT_INT;
            Sum             = Defaults.DEFAULT_INT;
            PayTime         = DateTime.Now;
            EmployeeId      = Defaults.DEFAULT_INT;
        }

        public TLPayment(int id, int bankAccountId, int sum, DateTime payTime, int employeeId)
        {
            Id              = id;
            Sum             = sum;
            BankAccountId   = bankAccountId;
            EmployeeId      = employeeId;
            PayTime         = payTime;
        }

        internal TLPayment(Payment payment)
        {
            Id              = payment.Id;
            Sum             = payment.Sum;
            BankAccountId   = payment.BankAccountId;
            PayTime         = Utilities.IsValidDateTime(payment.PayTime) ? Convert.ToDateTime(payment.PayTime) : DateTime.MinValue;
            EmployeeId      = payment.EmployeeId;
        }

        internal Payment ToPayment()
        {
            return new Payment(Id, BankAccountId, Sum, EmployeeId, PayTime.ToString(Defaults.DATETIME_FORMAT));
        }
    }

    #endregion

    #region Systems

    public class IdentificationSys : IIdentificationSys
    {
        private RepositoryManager RepositoryMan;

        public IdentificationSys(string login, string password)
        {
            RepositoryMan = RepositoryManager.GetRepositoryManager(login, password);
        }

        public RepositoryManager GetRepositoryManager()
        {
            return RepositoryMan;
        }
    }

    public class LoginSys : ILoginSys
    {
        private IIdentificationSys identificationSys;

        public LoginSys(string login, string password)
        {
            identificationSys = new IdentificationSys(login, password);
        }

        public bool LogIn()
        {
            try
            {
                return identificationSys.GetRepositoryManager().TimeTableRepository.LogIn();
            }
            catch { }

            return false;
        }

        public bool LogOut()
        {
            try
            {
                return identificationSys.GetRepositoryManager().TimeTableRepository.LogOut();
            }
            catch { }

            return false;
        }
    }

    public class PersonnelSys : IPersonnelSys
    {
        private IIdentificationSys identificationSys;

        public PersonnelSys(string login, string password)
        {
            identificationSys = new IdentificationSys(login, password);
        }
        
        public TLEmployee GetEmployee()
        {
            try
            {
                return new TLEmployee(identificationSys.GetRepositoryManager().EmployeeRepository.GetData());
            }
            catch { }

            return null;
        }

        public bool FinishTask(TLTask task)
        {
            try
            {
                return identificationSys.GetRepositoryManager().TaskRepository.FinishTask(task.ToTask());
            }
            catch { }

            return false;
        }

        public TLPosition GetPosition()
        {
            try
            {
                return new TLPosition(identificationSys.GetRepositoryManager().PositionRepository.GetData());
            }
            catch { }

            return null;
        }

        public bool AddEmployee(TLEmployee employee)
        {
            try
            {
                return identificationSys.GetRepositoryManager().EmployeeRepository.AddEmployee(employee.ToEmployee());
            }
            catch { }

            return false;
        }

        public bool EditEmployee(TLEmployee oldEmployee, TLEmployee newEmployee)
        {
            try
            {
                return identificationSys.GetRepositoryManager().EmployeeRepository.EditEmployee(oldEmployee.ToEmployee(), newEmployee.ToEmployee());
            }
            catch { }

            return false;
        }

        public bool DeleteEmployee(TLEmployee employee)
        {
            try
            {
                return identificationSys.GetRepositoryManager().EmployeeRepository.DeleteEmployee(employee.ToEmployee());
            }
            catch { }

            return false;
        }

        public IEnumerable<TLEmployee> GetAll()
        {
            try
            {
                List<TLEmployee> result = new List<TLEmployee>();

                foreach (Employee employee in identificationSys.GetRepositoryManager().EmployeeRepository.GetAll())
                {
                    result.Add(new TLEmployee(employee));
                }

                return result;
            }
            catch { }

            return null;
        }

        public bool AddTask(TLTask task)
        {
            try
            {
                return identificationSys.GetRepositoryManager().TaskRepository.AddTask(task.ToTask());
            }
            catch { }

            return false;
        }

        public bool DeleteTask(TLTask task)
        {
            try
            {
                return identificationSys.GetRepositoryManager().TaskRepository.DeleteTask(task.ToTask());
            }
            catch { }

            return false;
        }

        public bool EditTask(TLTask oldTask, TLTask newTask)
        {
            try
            {
                return identificationSys.GetRepositoryManager().TaskRepository.EditTask(oldTask.ToTask(), newTask.ToTask());
            }
            catch { }

            return false;
        }

        public bool CloseTask(TLTask task)
        {
            // TODO:

            return false;
        }

        public IEnumerable<TLTask> GetAllTasks()
        {
            try
            {
                List<TLTask> result = new List<TLTask>();

                foreach (Task task in identificationSys.GetRepositoryManager().TaskRepository.GetAll())
                {
                    result.Add(new TLTask(task));
                }

                return result;
            }
            catch { }

            return null;
        }

        public IEnumerable<TLPosition> GetAllPositions()
        {
            try
            {
                List<TLPosition> result = new List<TLPosition>();

                foreach (Position position in identificationSys.GetRepositoryManager().PositionRepository.GetAll())
                {
                    result.Add(new TLPosition(position));
                }

                return result;
            }
            catch { }

            return null;
        }
    }

    public class ReportSys : IReportSys
    {
        private IIdentificationSys identificationSys;

        public ReportSys(string login, string password)
        {
            identificationSys = new IdentificationSys(login, password);
        }

        public TLReport Get(DateTime startTime, DateTime endTime)
        {
            try
            {
                // TODO: add startTime & endTime handling

                List<TLWorkTime> timeTable = new List<TLWorkTime>();

                foreach (TimeTable item in identificationSys.GetRepositoryManager().TimeTableRepository.GetData())
                {
                    if (Utilities.IsValidDateTime(item.TimeOut))
                    {
                        DateTime timeIn = Convert.ToDateTime(item.TimeIn);
                        DateTime timeOut = Convert.ToDateTime(item.TimeOut);

                        if (timeIn >= startTime && timeOut <= endTime)
                        {
                            timeTable.Add(new TLWorkTime(item));
                        }
                    }
                    else
                    {
                        if (Convert.ToDateTime(item.TimeIn) >= startTime && Convert.ToDateTime(item.TimeIn) <= endTime)
                        {
                            timeTable.Add(new TLWorkTime(item));
                        }
                    }
                }

                return new TLReport(new TLEmployee(identificationSys.GetRepositoryManager().EmployeeRepository.GetData()), timeTable);
            }
            catch { }
            return null;
        }

        public IEnumerable<TLReport> GetAll(DateTime startTime, DateTime endTime)
        {
            try
            {
                List<TLReport> result = new List<TLReport>();

                IEnumerable<TimeTable> timetables = identificationSys.GetRepositoryManager().TimeTableRepository.GetAll();
                
                foreach (Employee employee in identificationSys.GetRepositoryManager().EmployeeRepository.GetAll())
                {
                    // TODO: add startTime & endTime handling

                    List<TLWorkTime> timeTable = new List<TLWorkTime>();

                    foreach (TimeTable item in timetables.Where(t => (t.EmployeeId == employee.Id) && Utilities.IsValidDateTime(t.TimeOut)  && (Convert.ToDateTime(t.TimeIn) >= startTime) && (Convert.ToDateTime(t.TimeOut) <= endTime)))
                    {
                        timeTable.Add(new TLWorkTime(item));
                    }

                    result.Add(new TLReport(new TLEmployee(employee), timeTable));
                }

                return result;
            }
            catch { }
            return null;
        }        
    }

    public class SalarySys : ISalarySys
    {
        private IIdentificationSys identificationSys;

        public SalarySys(string login, string password)
        {
            identificationSys = new IdentificationSys(login, password);
        }

        public int Get(DateTime startTime, DateTime endTime)
        {
            try
            {
                // TODO: Improve algorithm

                int fullHoursCount = 0;
                int fullMinCount   = 0;
                int sum            = 0;

                IEnumerable<TimeTable> timeTable = identificationSys.GetRepositoryManager().TimeTableRepository.GetData();
                
                foreach(TimeTable item in timeTable)
                {
                    if ((Utilities.IsValidDateTime(item.TimeIn) && Utilities.IsValidDateTime(item.TimeOut)) && Convert.ToDateTime(item.TimeIn) >= startTime && Convert.ToDateTime(item.TimeOut) <= endTime)
                    {
                        TimeSpan timeSpan = Convert.ToDateTime(item.TimeOut) - Convert.ToDateTime(item.TimeIn);

                        fullHoursCount += timeSpan.Hours;
                        fullMinCount   += timeSpan.Minutes;
                    }
                }

                IEnumerable<Task> tasks = identificationSys.GetRepositoryManager().TaskRepository.GetData();

                if (tasks != null)
                {
                    foreach (Task task in tasks)
                    {
                        if (Utilities.IsValidDateTime(task.StartTime) && Utilities.IsValidDateTime(task.EstimationTime))
                        {
                            if (Utilities.IsValidDateTime(task.EndTime) && (Convert.ToDateTime(task.EndTime) >= startTime && Convert.ToDateTime(task.EndTime) <= endTime))
                            {
                                if (Convert.ToDateTime(task.EndTime) <= Convert.ToDateTime(task.EstimationTime))
                                {
                                    sum += task.Cost;
                                }
                                else
                                {
                                    DateTime penaltyTime = (Convert.ToDateTime(task.EstimationTime) > startTime) ? Convert.ToDateTime(task.EstimationTime) : startTime;
                                    
                                    TimeSpan timeSpan = Convert.ToDateTime(task.EndTime) - penaltyTime;

                                    sum -= task.Penalty * timeSpan.Hours;
                                }
                            }
                            else if (!Utilities.IsValidDateTime(task.EndTime))
                            {
                                DateTime penaltyTime = (Convert.ToDateTime(task.StartTime) > startTime) ? Convert.ToDateTime(task.StartTime) : startTime;

                                TimeSpan timeSpan = endTime - penaltyTime;

                                sum -= task.Penalty * timeSpan.Hours;
                            }
                        }
                    }
                }

                sum += (fullHoursCount + fullMinCount / 60) * identificationSys.GetRepositoryManager().PositionRepository.GetData().Salary;

                return sum;
            }
            catch { }

            return Defaults.DEFAULT_INT;
        }

        public bool Pay(TLEmployee user)
        {
            try
            {
                IEnumerable<Payment> payments    = identificationSys.GetRepositoryManager().PaymentRepository.GetData(user.ToEmployee());
                DateTime             lastPayTime;

                if (payments == null)
                {
                    lastPayTime = DateTime.MinValue;
                }
                else
                {
                    lastPayTime = Convert.ToDateTime(payments.First().PayTime);
                }

                int sum = Get(user, lastPayTime, DateTime.Now);

                if (identificationSys.GetRepositoryManager().PaymentRepository.AddPayment(user.ToEmployee(), sum))
                {
                    return true;
                }
            }
            catch { }

            return false;
        }

        public bool PayAll()
        {
            try
            {
                foreach (Employee user in identificationSys.GetRepositoryManager().EmployeeRepository.GetAll())
                {
                    if (!Pay(new TLEmployee(user)))
                    {
                        return false;
                    }
                }
                return true;
            }
            catch { }

            return false;
        }

        public IDictionary<TLEmployee, int> GetAll(DateTime startTime, DateTime endTime)
        {
            try
            {
                Dictionary<TLEmployee, int> result = new Dictionary<TLEmployee,int>();

                IEnumerable<Employee> employees = identificationSys.GetRepositoryManager().EmployeeRepository.GetAll();

                if (employees != null)
                {
                    foreach (Employee user in employees)
                    {
                        TLEmployee tlUser = new TLEmployee(user);

                        result.Add(tlUser, Get(tlUser, startTime, endTime));
                    }
                }

                return result;
            }
            catch { } 
            
            return null;
        }

        public IDictionary<TLPayment, TLEmployee> GetAllPayments()
        {
            try
            {
                Dictionary<TLPayment, TLEmployee> result = new Dictionary<TLPayment,TLEmployee>();

                foreach (Employee user in identificationSys.GetRepositoryManager().EmployeeRepository.GetAll())
                {
                    IEnumerable<Payment> payments = identificationSys.GetRepositoryManager().PaymentRepository.GetData(user);

                    if (payments != null)
                    {
                        foreach (Payment payment in payments)
                        {
                            result.Add(new TLPayment(payment), new TLEmployee(user));
                        }
                    }
                }

                return result;
            }
            catch { }

            return null;
        }

        public int Get(TLEmployee employee, DateTime startTime, DateTime endTime)
        {
            try
            {
                // TODO: Improve algorithm

                int fullHoursCount = 0;
                int fullMinCount   = 0;
                int sum            = 0;

                IEnumerable<TimeTable> timeTable = identificationSys.GetRepositoryManager().TimeTableRepository.GetAll().Where(t => t.EmployeeId == employee.Id);
                
                foreach(TimeTable item in timeTable)
                {
                    if ((Utilities.IsValidDateTime(item.TimeIn) && Utilities.IsValidDateTime(item.TimeOut)) && Convert.ToDateTime(item.TimeIn) >= startTime && Convert.ToDateTime(item.TimeOut) <= endTime)
                    {
                        TimeSpan timeSpan = Convert.ToDateTime(item.TimeOut) - Convert.ToDateTime(item.TimeIn);

                        fullHoursCount += timeSpan.Hours;
                        fullMinCount   += timeSpan.Minutes;
                    }
                }

                IEnumerable<Task> tasks = identificationSys.GetRepositoryManager().TaskRepository.GetData();

                if (tasks != null)
                {
                    foreach (Task task in tasks)
                    {
                        if (Utilities.IsValidDateTime(task.StartTime) && Utilities.IsValidDateTime(task.EstimationTime))
                        {
                            if (Utilities.IsValidDateTime(task.EndTime) && (Convert.ToDateTime(task.EndTime) >= startTime && Convert.ToDateTime(task.EndTime) <= endTime))
                            {
                                if (Convert.ToDateTime(task.EndTime) <= Convert.ToDateTime(task.EstimationTime))
                                {
                                    sum += task.Cost;
                                }
                                else
                                {
                                    DateTime penaltyTime = (Convert.ToDateTime(task.EstimationTime) > startTime) ? Convert.ToDateTime(task.EstimationTime) : startTime;
                                    
                                    TimeSpan timeSpan = Convert.ToDateTime(task.EndTime) - penaltyTime;

                                    sum -= task.Penalty * timeSpan.Hours;
                                }
                            }
                            else if (!Utilities.IsValidDateTime(task.EndTime))
                            {
                                DateTime penaltyTime = (Convert.ToDateTime(task.StartTime) > startTime) ? Convert.ToDateTime(task.StartTime) : startTime;

                                TimeSpan timeSpan = endTime - penaltyTime;

                                sum -= task.Penalty * timeSpan.Hours;
                            }
                        }
                    }
                }

                sum += (fullHoursCount + fullMinCount / 60) * identificationSys.GetRepositoryManager().PositionRepository.GetData().Salary;

                return sum;
            }
            catch { }

            return Defaults.DEFAULT_INT;
        }
    }

    #endregion

    #region Users

    public class UserEmployee : IUserEmployee
    {
        protected ILoginSys     loginSys;
        protected IPersonnelSys personnelSys;
        protected IReportSys    reportSys;
        protected ISalarySys    salarySys;

        public UserEmployee(string login, string password)
        {
            loginSys     = new LoginSys(login, password);
            personnelSys = new PersonnelSys(login, password);
            reportSys    = new ReportSys(login, password);
            salarySys    = new SalarySys(login, password);
        }

        public bool LogIn()
        {
            return loginSys.LogIn();
        }

        public bool LogOut()
        {
            return loginSys.LogOut();
        }

        public TLEmployee GetEmployee()
        {
            return personnelSys.GetEmployee();
        }

        public bool FinishTask(TLTask task)
        {
            return personnelSys.FinishTask(task);
        }

        public TLPosition GetPosition()
        {
            return personnelSys.GetPosition();
        }

        public TLReport GetReport(DateTime startTime, DateTime endTime)
        {
            return reportSys.Get(startTime, endTime);
        }

        public int GetSalary(DateTime startTime, DateTime endTime)
        {
            return salarySys.Get(startTime, endTime);
        }
    }

    public class UserManager : UserEmployee, IManagerEmployee
    {
        public UserManager(string login, string password) :
            base (login, password)
        {
        }

        public bool AddEmployee(TLEmployee employee)
        {
            return personnelSys.AddEmployee(employee);
        }

        public bool EditEmployee(TLEmployee oldEmployee, TLEmployee newEmployee)
        {
            return personnelSys.EditEmployee(oldEmployee, newEmployee);
        }

        public bool DeleteEmployee(TLEmployee employee)
        {
            return personnelSys.DeleteEmployee(employee);
        }

        public IEnumerable<TLEmployee> GetAllEmployees()
        {
            return personnelSys.GetAll();
        }

        public bool AddTask(TLTask task)
        {
            return personnelSys.AddTask(task);
        }

        public bool DeleteTask(TLTask task)
        {
            return personnelSys.DeleteTask(task);
        }

        public bool EditTask(TLTask oldTask, TLTask newTask)
        {
            return personnelSys.EditTask(oldTask, newTask);
        }

        public bool CloseTask(TLTask task)
        {
            return personnelSys.CloseTask(task);
        }

        public IEnumerable<TLTask> GetAllTasks()
        {
            return personnelSys.GetAllTasks();
        }

        public IEnumerable<TLPosition> GetAllPositions()
        {
            return personnelSys.GetAllPositions();
        }

        public IEnumerable<TLReport> GetAllReports(DateTime startTime, DateTime endTime)
        {
            return reportSys.GetAll(startTime, endTime);
        }

        public bool Pay(TLEmployee user)
        {
            return salarySys.Pay(user);
        }

        public bool PayAll()
        {
            return salarySys.PayAll();
        }

        public IDictionary<TLEmployee, int> GetAllSalaries(DateTime startTime, DateTime endTime)
        {
            return salarySys.GetAll(startTime, endTime);
        }

        public IDictionary<TLPayment, TLEmployee> GetAllPayments()
        {
            return salarySys.GetAllPayments();
        }

        public int GetEmployeeSalary(TLEmployee employee, DateTime startTime, DateTime endTime)
        {
            return salarySys.Get(employee, startTime, endTime);
        }
    }

    #endregion
}
